// 创建生产环境配置文件
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const { appDist, appSrc } = require('./paths');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; // 打包体积分析
const TerserPlugin  = require('terser-webpack-plugin'); // 压缩JS（webpack5 自带，默认开启了多进程和缓存 parallel: true 配置）
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin"); // 将在Webpack构建期间搜索 CSS 文件，优化、压缩 CSS。
const glob = require('glob'); //glob 模块是一个用于匹配文件路径的工具
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin') // CSS摇树（清除未使用的css）
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 打包时CSS默认放在JS文件内, 该插件可以从webpack中分离CSS文件（然后再摇树）

module.exports = merge(common, {
    mode: 'production', // 生产模式会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production
    // 配置生产环境输出
    output: {
        filename: '[name].[contenthash].bundle.js', // [name]表示入口文件的名称, [contenthash]使用md4-hash文件内容生成的哈希值（文件未改变使用缓存，改变会更新缓存【充分利用浏览器缓存静态资源的效率来提高加载性能】）
        path: appDist,
        clean: true,
        pathinfo: false //默认 webpack 会在输出的 bundle 中生成路径信息，将路径信息删除可小幅提升构建速度。
        // publicPath: 'https://cdn.example.com/', // CDN 域名
    },
    plugins: [
        // 打包体积分析
        new BundleAnalyzerPlugin(),
        // 提取 CSS(生成环境下分离CSS)
        new MiniCssExtractPlugin({ // 将CSS提取到单独的文件中，为每个包含CSS的JS文件创建一个CSS文件，并且支持CSS和SourceMaps的按需加载。
            filename: "[hash].[name].css",
            // chunkFilename: '[id].css'
        }),
        // CSS Tree Shaking
        new PurgeCSSPlugin({
            paths: glob.sync(`${appSrc}/**/*`,  { nodir: true }),
        })
    ],
    module: {
        rules: [
            // 【处理CSS（CSS模块化+自动插入若干style节点）】
            {
                test: /\.css$/,
                include: appSrc,
                use: [
                    MiniCssExtractPlugin.loader,  // 将CSS与JS代码分离
                    // 将CSS转化成CommonJS模块(对@import和url()进行处理，让CSS也能模块化开发)
                    'css-loader',
                ]
            }
        ]
    },
    // 【优化配置】
    optimization: {
        runtimeChunk: true, // 为运行时代码创建一个额外的 chunk，减少 entry chunk 体积，提高性能
        // 体积最小化压缩
        minimizer: [
            // JS压缩
            new TerserPlugin({
                parallel: 4,  //使用4进程并发运行压缩以提高构建速度
                terserOptions: {
                    parse: {
                        ecma: 8,
                    },
                    compress: {
                        ecma: 5,
                        warnings: false,
                        comparisons: false,
                        inline: 2,
                    },
                    mangle: {
                        safari10: true,
                    },
                    output: {
                        ecma: 5,
                        comments: false,
                        ascii_only: true,
                    }
                }
            }),
            // CSS压缩
            new CssMinimizerPlugin({
                parallel: 4
            }),
        ],
        // 【抽离重复代码】将公共的模块单独打包，更好地管理代码拆分和模块共享，以优化资源加载和减少重复加载的代码
        splitChunks: {
            chunks: 'all', // 指定哪些块应该被优化，默认为async（异步块）
            minSize: 30000, // 模块的最小大小（字节），大于该值的模块才会被拆分
            // maxSize: 0, // 模块的最大大小（字节）, 不写就是对大小没有限制
            minChunks: 1, // 模块被引用的最小次数，只有超过该次数才会被拆分
            maxAsyncRequests: 10, // 按需加载时并行请求的最大数量
            maxInitialRequests: 3, // 入口点的并行请求数量
            automaticNameDelimiter: '~', // 自动命名拆分的块时的连接符
            name: (module, chunks, cacheGroupKey) => { // 配置拆分块的名字
                return module.name;
            },
            cacheGroups: { // 定义缓存组，可以根据不同的规则将模块分配到不同的拆分块中
                // vendor缓存组用于将来自node_modules目录的模块拆分到一个单独的vendor块中
                vendor: {
                    test: /[\\/]node_modules[\\/]/, // 匹配需要拆分的模块所在的目录
                    priority: -10, // 缓存组的优先级，值越大，优先级越高
                    name: 'vendor', // 拆分出来的模块名称
                    enforce: true
                },
                // default缓存组用于将其他满足条件的模块拆分到一个默认块中
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                }
            }
        },
        //让公共包 splitChunks 的 hash 不因为新的依赖而改变，减少非必要的 hash 变动（通过配置 contenthash/hash，浏览器缓存了未改动的文件，仅重新加载有改动的文件，大大加快加载速度）
        moduleIds: 'deterministic'
    }
})
